﻿using System;
using System.Reflection;
using nRoute.Components;
using nRoute.Components.Composition;
using nRoute.Utilities;

namespace nRoute.Mapping
{
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    public class MapServiceAttribute
		 : MapResourceBaseAttribute
    {
        private const string SERVICE_MUSTBE_OFTYPE = "{0} type must be an implementation of {1}.";

        private readonly Type _serviceType;
        private readonly string _name;
        private readonly Type[] _dependencies;

        public MapServiceAttribute(Type serviceType)
		 : this(serviceType, (string)null, null) { }

        public MapServiceAttribute(Type serviceType, params Type[] dependencies)
		 : this(serviceType, null, dependencies) { }

        public MapServiceAttribute(Type serviceType, string name, params Type[] dependencies)
        {
            Guard.ArgumentNotNull(serviceType, "serviceType");

            _serviceType = serviceType;
            _name = name;
            _dependencies = dependencies;
        }

#region Properties

        public string Name
        {
            get { return _name; }
        }

        public Type ServiceType
        {
            get { return _serviceType; }
        }

        public bool IsDefault { get; set; }

        public Type LifetimeManagerType { get; set; }

        public InitializationMode InitializationMode { get; set; }

#endregion

#region Overrides

        protected internal override bool IsDefaultResource
        {
            get { return IsDefault; }
        }

        protected internal override bool CanInitialize(Type targetType, IResourceCatalog resourceCatalog)
        {
            if (_dependencies == null || _dependencies.Length == 0) return true;
            
            // else we need to check if each of the dependency is available
            foreach (var _dependency in _dependencies)
            {
                if (!resourceCatalog.IsResourceable(_dependency)) return false;
            }
            return true;
        }

        protected internal override Type GetResourceType(Type targetType)
        {
            return _serviceType;
        }

        protected internal override IResourceLocator GetResourceLocator(Type targetType, ITypeBuilder typeBuilder)
        {
            EnsureIsServiceType(targetType, _serviceType);

            var _lifetimeManager = (LifetimeManagerType != null) ? (ILifetimeManager)typeBuilder.BuildType(this.LifetimeManagerType) : new PerInstanceLifetimeManager();
            return new DefaultServicesLocator(this.Name, this.ServiceType, targetType, _lifetimeManager, this.InitializationMode);
        }

#endregion

#region Helper
        
        protected static void EnsureIsServiceType(Type implementationType, Type serviceType)
        {
            if (!serviceType.IsAssignableFrom(implementationType)) throw new InvalidOperationException(
               string.Format(SERVICE_MUSTBE_OFTYPE, implementationType.FullName, serviceType.FullName));
        }

#endregion

    }
}
